#pragma once

#include <vex/iterable/range_traits.h>
#include <vex/iterable/make_intrusive_range.h>
#include <vex/iterable/stl_interop/range_from_iterator.h>
#include <vex/iterable/stl_interop/tti.h>
#include <vex/iterable/any_range.h>
#include <boost/utility/enable_if.hpp>
#include <iterator>

namespace vex {
    namespace iterable {

        namespace any {
            template<class TRange>
            struct any_range_from_range {
                typedef any_range<
                    typename range_traits<TRange>::category,
                    typename range_traits<TRange>::reference
                > type;
            };

            template<class TContainer>
            struct any_range_from_container {
                typedef typename stl_interop::make_range_from_container<TContainer>::type range_type;
                //typedef typename any_range_from_range<range_type>::type type;
                typedef any_range<
                    typename range_traits<range_type>::category,
                    typename range_traits<range_type>::reference
                > type;
            };

            template< class TItr, class TItrTraits = std::iterator_traits<TItr> >
            struct any_range_from_iterator {
                typedef typename stl_interop::make_range_from_iterator<TItr, TItrTraits>::type range_type;
                typedef any_range<
                    typename range_traits<range_type>::category,
                    typename range_traits<range_type>::reference
                > type;
            };

            //template <class T>
            //struct lazy_enable_if_range 
            //    : public boost::lazy_enable_if_c<is_range<T>::value, any_range_from_range<T> > {};

            //template <class T>
            //struct lazy_disable_if_range 
            //    : public boost::lazy_disable_if_c<is_range<T>::value, T> {};
        }

        /// <summary>
        /// Create <c>any_range</c> instance from instance of iterable range. 
        /// The type of the resulting <c>any_range</c> will be automatically 
        /// infered from passed parameter
        /// </summary>
        ///
        /// <param name="p_range">
        /// Instance of iterable range, must conform to concept of vex iterable range.
        /// </param>
        template<class TRange> auto make_any_range(TRange&& p_range)
            -> typename lazy_enable_if_range< TRange, any::any_range_from_range<TRange> >::type
        {
            return typename any::any_range_from_range<TRange>::type(
                make_raw_range(std::forward<TRange>(p_range)), false);
        }

        /// <summary>
        /// Create <c>any_range</c> instance from stl iterator range. 
        /// The type of the resulting <c>any_range</c> will be automatically 
        /// infered from passed parameters
        /// </summary>
        ///
        /// <param name="p_beg">
        /// Iterators which defines the begin of a range. Must conform to concept of stl iterator.
        /// </param>
        /// <param name="p_end">
        /// Iterators which defines the end of a range. Must conform to concept of stl iterator.
        /// </param>
        template<class TItr> auto make_any_range(TItr p_beg, TItr p_end)
            -> typename any::any_range_from_iterator<TItr>::type
            //-> decltype(::vex::iterable::make_any_range(::vex::iterable::make_range(p_beg, p_end)))
        {
            return make_any_range(make_range(
                std::forward<TItr>(p_beg),
                std::forward<TItr>(p_end)));
        }

        /// <summary>
        /// Create <c>any_range</c> instance from a type which resembles a stl container. 
        /// The type of the resulting <c>any_range</c> will be automatically 
        /// infered from passed parameter
        /// </summary>
        ///
        /// <param name="p_range">
        /// Instance of a type which resembles a stl container. Specifically, it must
        /// define internal type named "iterator" which conforms to a proper stl concept
        /// </param>
        template<class TContainer> auto make_any_range(TContainer const& p_container)
            -> typename boost::lazy_disable_if_c<
            is_range<TContainer>::value || std::is_array<TContainer>::value,
            any::any_range_from_container<TContainer>
            >::type
        {
            return make_any_range(make_range(p_container));
        }

        /// <summary>
        /// Create <c>any_range</c> instance from a type which resembles a stl container. 
        /// The type of the resulting <c>any_range</c> will be automatically 
        /// infered from passed parameter
        /// </summary>
        ///
        /// <param name="p_range">
        /// Instance of a type which resembles a stl container. Specifically, it must
        /// define internal type named "iterator" which conforms to a proper stl concept
        /// </param>
        template<class TContainer> auto make_any_range(TContainer& p_container)
            -> typename boost::lazy_disable_if_c<
            is_range<TContainer>::value || std::is_array<TContainer>::value,
            any::any_range_from_container<TContainer>
            >::type
        {
            return make_any_range(make_range(p_container));
        }

        /// <summary>
        /// Create <c>any_range</c> instance from a fixed size array. 
        /// The type of the resulting <c>any_range</c> will be automatically 
        /// infered from passed parameter
        /// </summary>
        ///
        /// <param name="p_range">
        /// l-value instance of a fixed size array
        /// </param>
        template<class T, std::size_t Size>
        auto make_any_range(T(&p_container)[Size])
            -> typename any::any_range_from_iterator<T*>::type
            //-> decltype(make_any_range(make_range(p_container)))
        {
            return make_any_range(make_range(p_container));
        }

        template<class T, std::size_t Size>
        auto make_any_range(const T(&p_container)[Size])
            -> typename any::any_range_from_iterator<T const*>::type
            //-> decltype(make_any_range(make_range(p_container)))
        {
            return make_any_range(make_range(p_container));
        }

        /// <summary>
        /// Create any_range instance from an instance of a type conforming to 
        /// stl output_iterator concept. The type of value to be iterated upon
        /// must be given as an explicit template argument
        /// </summary>
        ///
        /// <param name="p_beg">
        /// Instance of a type which represents the output iterator
        /// </param>
        /// <param name="p_tag">
        /// Instance of <c>std::output_iterator_tag</c>, used to disambiguate this overload
        /// </param>
        template<class TRef, class TOutItr> auto make_any_output_range(TOutItr p_beg)
            -> typename any::any_range_from_iterator<TOutItr, stl_interop::output_iterator_traits<TRef> >::type
            //-> decltype(make_any_range(make_output_range<TRef>(p_beg)))
        {
            return make_any_range(make_output_range<TRef>(p_beg));
        }
    }

    using iterable::make_any_range;
    using iterable::make_any_output_range;
}